```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Redis数据库集群架构深度解析</title>
    <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css">
    <link rel="stylesheet" href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', 'Noto Serif SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            background-color: #f8fafc;
            color: #1e293b;
            line-height: 1.6;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 20px;
        }
        .hero {
            background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
            color: white;
            padding: 6rem 0;
            margin-bottom: 3rem;
            border-radius: 0 0 20px 20px;
            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
        }
        .hero-title {
            font-size: 3rem;
            font-weight: 700;
            margin-bottom: 1rem;
        }
        .hero-subtitle {
            font-size: 1.5rem;
            font-weight: 300;
            opacity: 0.9;
            max-width: 800px;
            margin: 0 auto;
        }
        .card {
            background: white;
            border-radius: 12px;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
            padding: 2rem;
            margin-bottom: 2rem;
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
        }
        .card-title {
            font-size: 1.5rem;
            font-weight: 600;
            color: #4f46e5;
            margin-bottom: 1rem;
            border-bottom: 2px solid #e2e8f0;
            padding-bottom: 0.5rem;
        }
        .card-subtitle {
            font-size: 1.25rem;
            font-weight: 500;
            color: #64748b;
            margin-bottom: 1rem;
        }
        .highlight {
            background-color: #f5f3ff;
            padding: 0.2rem 0.4rem;
            border-radius: 4px;
            font-weight: 500;
            color: #7c3aed;
        }
        .code-block {
            background-color: #1e293b;
            color: #e2e8f0;
            padding: 1rem;
            border-radius: 8px;
            font-family: monospace;
            overflow-x: auto;
            margin: 1.5rem 0;
        }
        .step {
            display: flex;
            margin-bottom: 0.5rem;
            align-items: flex-start;
        }
        .step-number {
            background-color: #4f46e5;
            color: white;
            width: 24px;
            height: 24px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 1rem;
            flex-shrink: 0;
        }
        .section {
            margin-bottom: 4rem;
        }
        .section-title {
            font-size: 2rem;
            font-weight: 700;
            color: #1e293b;
            margin-bottom: 1.5rem;
            position: relative;
        }
        .section-title:after {
            content: "";
            position: absolute;
            bottom: -8px;
            left: 0;
            width: 60px;
            height: 4px;
            background-color: #4f46e5;
            border-radius: 2px;
        }
        .image-container {
            margin: 2rem 0;
            text-align: center;
        }
        .image-container img {
            max-width: 100%;
            border-radius: 8px;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
        }
        .icon {
            margin-right: 0.5rem;
            color: #4f46e5;
        }
        .note {
            background-color: #f0f9ff;
            border-left: 4px solid #60a5fa;
            padding: 1rem;
            margin: 1.5rem 0;
            border-radius: 0 8px 8px 0;
        }
        .note-title {
            font-weight: 600;
            margin-bottom: 0.5rem;
            color: #1e40af;
        }
        .grid-container {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 2rem;
            margin: 2rem 0;
        }
        .feature-card {
            background: white;
            border-radius: 12px;
            padding: 1.5rem;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
        }
        .feature-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
        }
        .feature-icon {
            font-size: 2rem;
            color: #4f46e5;
            margin-bottom: 1rem;
        }
        .feature-title {
            font-size: 1.25rem;
            font-weight: 600;
            margin-bottom: 0.5rem;
            color: #1e293b;
        }
        .feature-desc {
            color: #64748b;
        }
        .quote {
            font-style: italic;
            font-size: 1.25rem;
            color: #64748b;
            border-left: 4px solid #c7d2fe;
            padding-left: 1.5rem;
            margin: 2rem 0;
        }
    </style>
</head>
<body>
    <div class="hero">
        <div class="container text-center">
            <h1 class="hero-title">Redis数据库集群架构深度解析</h1>
            <p class="hero-subtitle">探索主从复制、读写分离与哨兵模式的高可用解决方案</p>
        </div>
    </div>

    <div class="container">
        <section class="section">
            <h2 class="section-title"><i class="fas fa-database icon"></i>1. 数据库集群核心概念</h2>
            
            <div class="card">
                <h3 class="card-title">1.1 有状态/无状态</h3>
                <p>在分布式系统中，服务可以分为有状态(Stateful)和无状态(Stateless)两种类型：</p>
                <div class="grid-container">
                    <div class="feature-card">
                        <div class="feature-icon"><i class="fas fa-server"></i></div>
                        <h4 class="feature-title">有状态服务</h4>
                        <p class="feature-desc">需要持久化存储数据，保持会话状态，如数据库、Redis等服务</p>
                    </div>
                    <div class="feature-card">
                        <div class="feature-icon"><i class="fas fa-bolt"></i></div>
                        <h4 class="feature-title">无状态服务</h4>
                        <p class="feature-desc">不需要保存数据状态，每个请求独立处理，如API网关、静态服务</p>
                    </div>
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">1.2 主从复制</h3>
                <h4 class="card-subtitle">1.2.1 基本概念</h4>
                <p><span class="highlight">主从复制是一种数据备份的方案</span>。简单来说，是使用两个或两个以上相同的数据库，将一个数据库当做主数据库，而另一个数据库当做从数据库。在主数据库中进行相应操作时，从数据库记录下所有主数据库的操作，使其二者一模一样。</p>
                
                <div class="note">
                    <div class="note-title"><i class="fas fa-lightbulb"></i> 为什么需要主从复制？</div>
                    <p>Redis持久化保证了即使redis服务重启也不会丢失数据，因为redis服务重启后会将硬盘上持久化的数据恢复到内存中，但是当redis服务器的硬盘损坏了可能会导致数据丢失，如果通过redis的主从复制机制就可以避免这种单点故障。Redis虽然读取写入的速度都特别快，但是也会产生读压力特别大的情况。为了分担读压力，Redis支持主从复制。</p>
                </div>

                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597871202-c2d21911-f274-480b-bc79-70a8a7ab6481.png" alt="主从复制示意图">
                </div>

                <h4 class="card-subtitle">1.2.2 主从复制6个步骤</h4>
                <div class="steps">
                    <div class="step">
                        <div class="step-number">1</div>
                        <div>从节点保存主节点信息</div>
                    </div>
                    <div class="step">
                        <div class="step-number">2</div>
                        <div>主从建立socket连接</div>
                    </div>
                    <div class="step">
                        <div class="step-number">3</div>
                        <div>从节点发送ping命令，等待主节点回应</div>
                    </div>
                    <div class="step">
                        <div class="step-number">4</div>
                        <div>权限验证，比如密码校验</div>
                    </div>
                    <div class="step">
                        <div class="step-number">5</div>
                        <div>主从连接正常后，开始同步数据集，首次建立复制，是<span class="highlight">全量复制</span>的方式</div>
                    </div>
                    <div class="step">
                        <div class="step-number">6</div>
                        <div>持续的主从复制，后续主节点发生数据变更，会继续给从节点发送命令，此处采用<span class="highlight">增量复制</span></div>
                    </div>
                </div>

                <h4 class="card-subtitle">1.2.3 全量复制</h4>
                <p>Redis全量复制一般发生在Slave初始化阶段，这时Slave需要将Master上的所有数据都复制一份。具体步骤如下：</p>
                
                <div class="code-block">
                    1. 从服务器连接主服务器，发送SYNC命令；<br>
                    2. 主服务器接收到SYNC命名后，开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令；<br>
                    3. 主服务器BGSAVE执行完后，向所有从服务器发送快照文件，并在发送期间继续记录被执行的写命令；<br>
                    4. 从服务器收到快照文件后丢弃所有旧数据，载入收到的快照；<br>
                    5. 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令；<br>
                    6. 从服务器完成对快照的载入，开始接收命令请求，并执行来自主服务器缓冲区的写命令；
                </div>

                <h4 class="card-subtitle">1.2.4 增量复制</h4>
                <p>Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。</p>
                <p>增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令，从服务器接收并执行收到的写命令。</p>
                <div class="quote">
                    总结增量同步就是当 master 服务器有数据更新的时候，会立刻同步到所有的 slave 服务器
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">1.3 读写分离</h3>
                <p>读写分离是一种让数据库更稳定的的使用数据库的方法。是在有主从数据库的情况下使用，将主数据库进行写操作，将查询的任务交给从数据库。</p>
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597871432-3a18bfb0-1aab-425a-8f9f-12ac7e01d862.png" alt="读写分离示意图">
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">1.4 哨兵模式</h3>
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/gif/21449790/1652597871890-369c1410-804b-412b-ac16-f7ee02d7e240.gif" alt="哨兵模式动图">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597872218-ec64f64c-7d77-4b99-abf7-14f7810380b7.png" alt="哨兵模式示意图">
                </div>
                <p>哨兵机制是一种容灾方案。哨兵：实则是一个在特殊模式下的Redis服务器，里面存储的是自己本身的信息，主服务器的信息，从服务器的信息。用一个或者多个哨兵来监视主服务器（也就是进行写操作的服务器）是否在正常执行任务，一旦哨兵发现主服务器不可用时，就找到一个合适的从服务器成为主服务器。</p>
                
                <p>哨兵是对Redis的系统的运行情况的监控，它是一个独立进程，功能有二个：</p>
                <ul class="list-disc pl-5 my-4">
                    <li class="mb-2">监控主数据库和从数据库是否运行正常；</li>
                    <li>主数据出现故障后自动将从数据库转化为主数据库；</li>
                </ul>
            </div>

            <div class="card">
                <h3 class="card-title">1.5 为什么使用主从复制和读写操作？</h3>
                <p>大多数互联网业务，往往<span class="highlight">读多写少</span>，这时候，数据库的读会首先成为数据库的瓶颈，这时，如果我们希望能够线性的提升数据库的读性能，消除读写锁冲突从而提升数据库的写性能，那么就可以使用读写分离架构。</p>
                
                <p class="quote">用一句话概括，读写分离是用来解决数据库的读性能瓶颈的。</p>

                <div class="grid-container">
                    <div class="feature-card">
                        <div class="feature-icon"><i class="fas fa-sync-alt"></i></div>
                        <h4 class="feature-title">主从复制优势</h4>
                        <ul class="list-disc pl-5">
                            <li>当主数据库出现问题时，可以当从数据库代替主数据库，可以避免数据的丢失</li>
                            <li>是一种备份方案</li>
                        </ul>
                    </div>
                    <div class="feature-card">
                        <div class="feature-icon"><i class="fas fa-random"></i></div>
                        <h4 class="feature-title">读写分离优势</h4>
                        <ul class="list-disc pl-5">
                            <li>避免从数据库进行写操作而导致的主从数据库数据不一致的情况</li>
                            <li>减轻主数据库的压力</li>
                        </ul>
                    </div>
                </div>

                <div class="mt-6 p-4 bg-gradient-to-r from-purple-500 to-indigo-600 text-white rounded-lg">
                    <h4 class="text-xl font-bold mb-2">高可用，高性能，高并发</h4>
                    <p>通过主从复制和读写分离架构，可以实现系统的高可用性、高性能处理和高并发支持。</p>
                </div>
            </div>
        </section>

        <section class="section">
            <h2 class="section-title"><i class="fas fa-terminal icon"></i>2. Redis主从复制实践</h2>
            
            <div class="card">
                <h3 class="card-title">2.1 拉取Redis镜像</h3>
                <div class="code-block">
                    docker pull redis
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">2.2 制作带有配置文件的镜像</h3>
                <p>基于刚才拉取的镜像(该镜像没有配置文件,后面要用到Redis配置的文件)文件再做一个带有Redis配置文件的镜像。</p>
                
                <div class="code-block">
                    1. 在/usr/local/soft/下面创建一个redis文件夹<br>
                    2. 从Redis的解压文件中拷贝redis.conf和sentinel.conf(哨兵配置文件)这两个文件到/usr/local/soft/redis
                </div>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597872580-17d1aec6-3d01-4db9-acc5-a53b7ca0392d.png" alt="配置文件拷贝">
                </div>

                <p>3. 在/usr/local/soft/redis下面新加一个文件Dockerfile(用来制作镜像的文件)</p>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597872803-32b74961-5520-4feb-8e39-83671169fe8a.png" alt="Dockerfile创建">
                </div>

                <h4 class="card-subtitle mt-4">DockerFile-redis内容如下：</h4>
                <div class="code-block">
                    FROM redis<br>
                    COPY redis.conf /usr/local/etc/redis/redis.conf<br>
                    VOLUME /usr/local/etc/redis<br>
                    CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
                </div>

                <h4 class="card-subtitle mt-4">4. 通过DockerFile构建一个镜像</h4>
                <div class="code-block">
                    docker build -f ./DockerFile-redis -t redis-conf .
                </div>
                <div class="mt-4">
                    <p><span class="highlight">Build</span> -- 执行镜像</p>
                    <p><span class="highlight">-f</span> -- DockeFile文件路径</p>
                    <p><span class="highlight">-t</span> -- 镜像名称</p>
                    <p><span class="highlight">.</span> -- 当前路径</p>
                </div>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597872985-2d6c879d-14d6-4869-8b48-a92fada21a30.png" alt="构建镜像">
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">2.3 创建一个网卡</h3>
                <p>我们Redis要做集群，Redis之间要相互通讯，所以我们需要创建一个网卡来通讯。</p>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597873209-fb773524-fe23-4afe-a594-ef8e23d1bc52.png" alt="创建网卡">
                </div>
                
                <div class="note">
                    <div class="note-title"><i class="fas fa-exclamation-circle"></i> 注意</div>
                    <p>如果创建网卡失败，重启docker服务。</p>
                </div>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597873447-de16bfb6-b308-42f9-8954-d130bb2d5ec3.png" alt="网卡创建成功">
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">2.4 基于镜像运行容器</h3>
                <div class="code-block">
                    docker run -d --name master-redis -p 6379:6379 --network=host redis-conf
                </div>
                
                <div class="note">
                    <div class="note-title"><i class="fas fa-exclamation-circle"></i> 注意</div>
                    <p>这里需要提前关闭本机Reids,否则端口号冲突导致容器创建失败。</p>
                </div>
                
                <p>容器创建好后需要关注一下信息：</p>
                
                <div class="grid-container mt-4">
                    <div class="feature-card">
                        <div class="feature-icon"><i class="fas fa-network-wired"></i></div>
                        <h4 class="feature-title">1. 容器的IP</h4>
                        <div class="image-container">
                            <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597873687-620fa290-e855-4805-bf5a-a70a1778426f.png" alt="容器IP">
                        </div>
                    </div>
                    <div class="feature-card">
                        <div class="feature-icon"><i class="fas fa-hdd"></i></div>
                        <h4 class="feature-title">2. 容器和宿主机映射的数据卷</h4>
                        <div class="image-container">
                            <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597873934-ad5047c7-b67b-4e4d-9878-42f8d87212bf.png" alt="数据卷映射">
                        </div>
                    </div>
                </div>
                
                <h4 class="card-subtitle mt-4">在数据卷中可以找到reids.conf配置的文件,需要设置以下几项信息：</h4>
                
                <div class="grid-container mt-4">
                    <div class="feature-card">
                        <h4 class="feature-title">1. 关闭保护模式</h4>
                        <div class="image-container">
                            <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597874113-759027f1-a3c8-47aa-85ee-5083d8bb7ef2.png" alt="关闭保护模式">
                        </div>
                    </div>
                    <div class="feature-card">
                        <h4 class="feature-title">2. 设置让其他机器也能连接reids</h4>
                        <div class="image-container">
                            <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597874353-d2284dde-d912-4afc-9887-9c1e91e4e6e6.png" alt="允许远程连接">
                        </div>
                    </div>
                    <div class="feature-card">
                        <h4 class="feature-title">3. Reids的守护线程要设置为no</h4>
                        <div class="image-container">
                            <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597874549-edae31cc-bc92-4bad-b9d0-49dfb58b8346.png" alt="守护线程设置">
                        </div>
                    </div>
                </div>
                
                <div class="note mt-4">
                    <div class="note-title"><i class="fas fa-info-circle"></i> 说明</div>
                    <p>修改完配置文件需要重启容器。以上操作就是一个主的Redis搭建成功，下面就可以搭建从的Reids服务器了,从的Redis服务器可以根据刚才创建的镜像来搭建，相对比较简单一些。</p>
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">2.5 搭建两个从的Redis服务器</h3>
                
                <h4 class="card-subtitle">2.5.1 搭建一个从服务器--slave1</h4>
                <div class="code-block">
                    docker run -d --name slave1-redis -p 6380:6379 --network=redis-network redis-conf
                </div>
                
                <p>1. 实例化容器</p>
                <p>2. 在容器详情中获取容器IP和宿主机映射的数据卷</p>
                <p>3. 从数据卷中找到slave1-redis的配置文件，修改三项内容(关闭保护模式,守护线程设置no,关闭本地绑定)</p>
                
                <h4 class="card-subtitle mt-4">2.5.2 搭建二个从服务器--slave2</h4>
                <div class="code-block">
                    docker run -d --name slave2-redis -p 6381:6379 --network=redis-network redis-conf
                </div>
                
                <p>修改的内容和上面是一样的</p>
            </div>

            <div class="card">
                <h3 class="card-title">2.6 配置主从复制</h3>
                <p>Master需要把自己的数据同步到两个slave中，所以要在两个slave中配置master的ip和端口号。两个slave都要做以下配置的，配置成功后需要重启容器。可以通过redis的<span class="highlight">info replication命令查看当前Redis角色。</span></p>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597874757-bc08b13a-b7d7-41cc-b066-05df6931cd3b.png" alt="主从配置">
                </div>
            </div>
        </section>

        <section class="section">
            <h2 class="section-title"><i class="fas fa-shield-alt icon"></i>3. 哨兵模式实践</h2>
            
            <div class="card">
                <h3 class="card-title">3.1 创建哨兵镜像文件</h3>
                <p>1. 在/usr/local/soft/redis下面创建一个DockerFile-sentinel文件(这个文件可以拿之前的DockerFile-redis文件来改)，内容如下</p>
                
                <div class="code-block">
                    FROM redis<br>
                    COPY sentinel.conf /usr/local/etc/redis/sentinel.conf<br>
                    VOLUME /usr/local/etc/redis<br>
                    EXPOSE 26379<br>
                    CMD [ "redis-sentinel", "/usr/local/etc/redis/sentinel.conf" ]
                </div>
                
                <p>2. 创建镜像</p>
                <div class="code-block">
                    docker build -f ./DockerFile-sentinel -t sentinel-conf .
                </div>
            </div>

            <div class="card">
                <h3 class="card-title">3.2 根据镜像实例化三个哨兵容器</h3>
                
                <h4 class="card-subtitle">Sentinel1</h4>
                <p>1. 实例化容器</p>
                <div class="code-block">
                    docker run -d --name redis-sentinel1 -p 26379:26379 --network=host sentinel-conf
                </div>
                
                <p>2. 通过命令查看ip和映射的数据卷</p>
                <p>3. 在sentinel.conf中配置哨兵被监听观察的Master</p>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597874998-af805a78-d576-4908-a234-bfecf0591974.png" alt="哨兵配置">
                </div>
                
                <p>4. 重启容器</p>
                
                <h4 class="card-subtitle mt-4">Sentinel2</h4>
                <div class="code-block">
                    docker run -d --name redis-sentinel2 -p 26380:26380 --network=host sentinel-conf
                </div>
                
                <h4 class="card-subtitle mt-4">Sentinel3</h4>
                <div class="code-block">
                    docker run -d --name redis-sentinel3 -p 26381:26381 --network=host sentinel-conf
                </div>
            </div>
        </section>

        <section class="section">
            <h2 class="section-title"><i class="fas fa-plug icon"></i>4. SpringBoot整合哨兵模式</h2>
            
            <div class="card">
                <p>在springboot配置文件中配置如下信息即可</p>
                
                <div class="code-block">
                    spring.redis.sentinel.master=mymaster<br>
                    spring.redis.sentinel.nodes[0]=localhost:26379<br>
                    spring.redis.sentinel.nodes[1]=localhost:26380<br>
                    spring.redis.sentinel.nodes[2]=localhost:26381
                </div>
                
                <div class="image-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597875160-e8c952d7-af88-4101-8378-98d6434f71a7.png" alt="SpringBoot配置">
                </div>
            </div>
        </section>
    </div>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>
```